module dungeon.monster;

import std.stdio;
import std.array;
import std.math;

import derelict.opengl.gl;

import nade.time;
import nade.vector;
import nade.angle;
import nade.color;
import nade.primitive;
import nade.sprite;
import nade.ast;
import nade.loader;
import nade.random;

import dungeon.game;
import dungeon.map;
import dungeon.map_object;
import dungeon.mover;
import dungeon.character;
import dungeon.attack;


class Monster {
	this(Game game)
	{
		_game = game;
	}

	MonsterObject spawn(Map map, const Vector2i coord)
	{
		return new MonsterObject(map, coord, this);
	}

	string id() { return _id; }
	string name() { return _name; }
	Sprite sprite() { return _sprite; }
	string description() { return _description; }
	real idleMin() { return _idleMin; }
	real idleMax() { return _idleMax; }

	protected {
		Game			_game;
		string			_id, _name;
		Sprite			_sprite;
		string			_description = "";
		real			_idleMin, _idleMax;
		Attack			_attack;
	}

	static void install()
	{
		Loader!(Monster).add("name",
			(ref Monster monster, AST ast, Object[string] state)
			{
				monster._id = ast[0].as!string;
				monster._name = ast[1].as!string;
				monster._game.addMonster(monster);
			}
		);

		Loader!(Monster).add("sprite",
			(ref Monster monster, AST ast, Object[string] state) { monster._sprite = new Sprite("Textures\\Sprites\\Monsters\\" ~ ast[0].as!string ~ ".bmp"); }
		);

		Loader!(Monster).add("description",
			(ref Monster monster, AST ast, Object[string] state) { monster._description = ast[0].as!string; }
		);

		Loader!(Monster).add("idle",
			(ref Monster monster, AST ast, Object[string] state)
			{
				monster._idleMin = ast[0].as!real;
				monster._idleMax = ast[1].as!real;
			}
		);

		Loader!(Monster).add("attack",
			(ref Monster monster, AST ast, Object[string] state) {
				monster._attack = monster._game.getAttack(ast, state);
			}
		);
	}
}


class MonsterObject : CharacterObject {
	this(Map map, const Vector2i coord, Monster monster)
	{
		super(map, new Character(map.game));
		enterTile(coord);
		_mover.from = _mover.to = coord;

		_monster = monster;
		_character.sprite = _monster.sprite;
		_nextMove = curTime + random(_monster.idleMin, _monster.idleMax);
		_nextAttack = curTime;
	}

	override void iterate()
	{
		void arrived(const Vector2i previous, const Vector2i current)
		{
			leaveTile(previous);
			_mover.travel = 0;
			_moving = false;

			_nextMove = curTime + random(_monster.idleMin, _monster.idleMax);
		}

		if(_moving)
		{
			_mover.move(frameTime*3, &arrived);
		}
		else if(curTime >= _nextMove)
		{
			static directions = [ Vector2i(1, 0), Vector2i(-1, 0), Vector2i(0, 1), Vector2i(0, -1) ];
			const dest = _mover.from + directions[random(0, 4)];
			if(_map.isPassable(dest))
			{
				enterTile(dest);
				_moving = true;
				_mover.to = dest;
			}
		}

		if(_target !is null  &&  _nextAttack < curTime)
		{
			if(_target.character.stat("HP") <= 0)
			{
				_target = null;
			}
			else
			{
				auto attack = _monster._attack;
				if(attack !is null  &&  _map.trace(position, _target.position)  &&  (_target.position - position).modulus <= attack.range)
				{
					_nextAttack = curTime + random(attack.delayMin, attack.delayMax);
					new AttackObject(_map, attack, this, _target);
				}
			}
		}
	}

	override void attacked(CharacterObject attacker, real damage)
	{
		super.attacked(attacker, damage);

		_target = attacker;
	}

	Monster monster() { return _monster; }

	protected {
		Monster				_monster;
		real				_nextMove, _nextAttack;
		CharacterObject		_target = null;
	}
}
